#include "PokerLogic.h"

class PokerLogicCacheManager {
public:
    PokerLogicCacheManager() {
        PokerLogic::shard();
    }
    ~PokerLogicCacheManager() {
        PokerLogic::release();
    }
};
static PokerLogicCacheManager s_thePokerLogicCacheManager;
static PokerLogic* s_thePokerLogic = nullptr;

PokerLogic * PokerLogic::shard() {
    if (s_thePokerLogic == nullptr) {
        s_thePokerLogic = new PokerLogic();
    }
    return s_thePokerLogic;
}
void PokerLogic::release() {
    delete s_thePokerLogic;
    s_thePokerLogic = nullptr;
}

static int32_t NUM_XW = 13;
static int32_t NUM_DW = 14;

PokerLogic::PokerLogic()
{
    // 最多3个人参与
    m_maxPlayerCount = 3;
    // 54张牌
    // 牌面：3, 4, 5, 6, 7, 8, 9, 10, J, Q,  K,  A,  2, XW, DW
    // 索引：0, 1, 2, 3, 4, 5, 6, 7,  8, 9, 10, 11, 12, 13, 14
    m_cards = { 
        500 + NUM_XW, // 小猫
        500 + NUM_DW, // 大猫
        //3, 4,  5,  6,  7,  8,  9, 10,  J,  Q,  K,  A,  2
        100,101,102,103,104,105,106,107,108,109,110,111,112,    // 黑桃
        200,201,202,203,204,205,206,207,208,209,210,211,212,    // 红桃
        300,301,302,303,304,305,306,307,308,309,310,311,312,    // 梅花
        400,401,402,403,404,405,406,407,408,409,410,411,412     // 方块
    };
}

PokerLogic::~PokerLogic() {
}

void PokerLogic::setMasterAndStart(uint8_t seat) {
    // 设定主角
    this->setMasterSeat(seat);
    // 然后把剩下的三张底牌都给他
    std::shared_ptr<PokerPlayer> player = this->getPlayer(seat);
    player->cardIn(this->popCard());
    player->cardIn(this->popCard());
    player->cardIn(this->popCard());
    // 开始出牌
    this->start();
}

void PokerLogic::getBottomCard(int32_t& a, int32_t& b, int32_t& c) {
    auto count = this->m_cards.size();
    if(count >= 3) {
        a = this->m_cards[count - 3];
        b = this->m_cards[count - 2];
        c = this->m_cards[count - 1];
    }
}

void PokerLogic::over() {
    // 牌局结算
    BaseLogic<PokerPlayer>::over();
    for (uint8_t i = 0; i < m_maxPlayerCount; ++i) {
        std::shared_ptr<PokerPlayer> player = this->getPlayer(i);
        if (player != nullptr) {
            player->over();
        }
    }
}

void PokerLogic::onceMore() {
    std::lock_guard<std::recursive_mutex> l(this->m_mutex);
    BaseLogic<PokerPlayer>::onceMore();
    for (uint8_t i = 0; i < m_maxPlayerCount; ++i) {
        std::shared_ptr<PokerPlayer> player = this->getPlayer(i);
        if (player != nullptr) {
            player->onceMore();
        }
    }
}

// 拆牌算法
void PokerLogic::doAnalysis(std::vector< int32_t >& theCards, std::vector<CARDS_DATA>& theCardGroups)
{
    theCardGroups.clear();
    std::vector<int32_t> vec_poke;
    std::vector<int32_t> diff;
    // https://edmundv.home.xs4all.nl/blog/2014/01/28/use-auto-and-and-for-range-based-for-loops/
    // The trick is to use auto&&
    // It works with both const and non-const containers, but also works with proxy objects.
    // It is always correct and efficient!
    for (auto&& one : theCards) {
        vec_poke.emplace_back(one % 100);//去掉花色，留下数字
    }
    // 牌面：3, 4, 5, 6, 7, 8, 9, 10, J, Q,  K,  A,  2, XW, DW
    // 索引：0, 1, 2, 3, 4, 5, 6, 7,  8, 9, 10, 11, 12, 13, 14
    std::sort(vec_poke.begin(), vec_poke.end());
//#if COCOS2D_DEBUG > 0
//    const char* tmp[] = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2", "XW", "DW"};
//    std::cout << "doAnalysis: ";
//    for (auto&& i : vec_poke) std::cout << tmp[i] << ", ";
//    std::cout << std::endl;
//#endif
    // 4个辅助数组
    std::vector<int32_t> card_1, card_2, card_3, card_4;
    // ======================== 将所有手牌分到四个辅助数组 ========================
    for(int32_t& oneCard : vec_poke) {
        auto itor_find = std::find(card_1.begin(), card_1.end(), oneCard);// 第一个数组，莫非是单牌？
        if (itor_find != card_1.end()) {
            itor_find = std::find(card_2.begin(), card_2.end(), oneCard);// 第二个数组，存在2个，对子是也
            if (itor_find != card_2.end()) {
                itor_find = std::find(card_3.begin(), card_3.end(), oneCard);// 第三个数组，至少三张，飞机是也
                if (itor_find != card_3.end()) {
                    card_4.emplace_back(oneCard);// 第四个数组，四个都有，炸弹是也
                }  else {
                    card_3.emplace_back(oneCard);
                }
            } else  {
                card_2.emplace_back(oneCard);
            }
        }  else {
            card_1.emplace_back(oneCard);
        }
    }
    // ======================== 从四个辅助数组中判断牌型 ========================
    // 火箭or王炸 (一张大王加一张小王构成，大过任何一种牌型)
    if (std::find(card_1.begin(), card_1.end(), NUM_XW) != card_1.end()
        && std::find(card_1.begin(), card_1.end(), NUM_DW) != card_1.end())  {
        theCardGroups.push_back({CARD_TYPE::MISSILE_CARD, {NUM_XW, NUM_DW}});
    }
    // 第4个数组中都是炸弹，出的时候可以当炸弹出，也可以带单张或者对子出
    for(int32_t& oneCard : card_4) {
        theCardGroups.push_back({CARD_TYPE::BOMB_CARD, {oneCard, oneCard, oneCard, oneCard}});
    }
    // 第3个数组中都是三张相同的，出的时候可以三不带，三带一，三带对
    diff.clear();
    std::set_symmetric_difference(card_4.begin(),card_4.end(),card_3.begin(),card_3.end(),std::back_inserter(diff));
    // card_4 和 card_3 对比，留下的是纯粹的飞机
    for(int32_t& oneCard : diff) {
        theCardGroups.push_back({CARD_TYPE::THREE_CARD, {oneCard, oneCard, oneCard}});
    }
    // 第2个数组中都是对子
    diff.clear();
    std::set_symmetric_difference(card_2.begin(),card_2.end(),card_3.begin(),card_3.end(),std::back_inserter(diff));
    // card_2 和 card_3 对比，留下的是纯粹的对子
    for(int32_t& oneCard : diff) {
        theCardGroups.push_back({CARD_TYPE::DOUBLE_CARD, {oneCard, oneCard}});
    }
    // ======================== 检查顺子，连对，飞机 ========================
    // 牌面：3, 4, 5, 6, 7, 8, 9, 10, J, Q,  K,  A,  2, XW, DW
    // 索引：0, 1, 2, 3, 4, 5, 6, 7,  8, 9, 10, 11, 12, 13, 14
    auto checkContinuity = [](std::vector<int32_t>& cardList, std::vector<CARDS_DATA>& theCardGroups, CARD_TYPE type, int space, int size) {
        int startIndex = 0;
        int count = (int)cardList.size();
        CARDS_DATA data = {type, {}};
        for(int i = 1; i < count; ++i) {
            if((cardList[i] != cardList[i - 1] + 1) || (cardList[i] >= 12) || (i == count - 1)) {
                // 断开了?
                int flag = ((cardList[i] == cardList[i - 1] + 1) && (cardList[i] < 12)) ? 1 : 0;
                // 计算够不够个数
                if(i - space + flag >= startIndex) {
                    data._cards.clear();
                    for(auto ii = startIndex; ii <= i - 1 + flag; ++ii) {
                        data._cards.emplace_back(cardList[ii]);
                        if(size >= 2) data._cards.emplace_back(cardList[ii]);
                        if(size >= 3) data._cards.emplace_back(cardList[ii]);
                        if(size >= 4) data._cards.emplace_back(cardList[ii]);
                    }
                    theCardGroups.push_back(data);
                }
                startIndex = i;
            }
            if(cardList[i] >= 12) {
                break;
            }
        }
    };
    // 顺子至少5张连续大小，2和怪不能用
    checkContinuity(card_1, theCardGroups, CARD_TYPE::SINGLE_CARD, 5, 1);
    checkContinuity(card_2, theCardGroups, CARD_TYPE::SINGLE_CARD, 5, 1);
    checkContinuity(card_3, theCardGroups, CARD_TYPE::SINGLE_CARD, 5, 1);
    checkContinuity(card_4, theCardGroups, CARD_TYPE::SINGLE_CARD, 5, 1);
    // 连对（至少3组两张大小相同的牌）
    checkContinuity(card_2, theCardGroups, CARD_TYPE::DOUBLE_CARD, 3, 2);
    checkContinuity(card_4, theCardGroups, CARD_TYPE::DOUBLE_CARD, 3, 2);
    // 飞机（连三张需要至少2组三张大小相同的牌）
    checkContinuity(card_3, theCardGroups, CARD_TYPE::THREE_CARD, 2, 3);
    // 大飞机（连四张需要至少2组四张大小相同的牌）
    checkContinuity(card_4, theCardGroups, CARD_TYPE::BOMB_CARD, 2, 4);
    // 可能拆掉太多飞机的顺子，要扼杀掉
    auto iterPlaneCheck = theCardGroups.begin();
    while(iterPlaneCheck != theCardGroups.end()) {
        if(iterPlaneCheck->_type == CARD_TYPE::SINGLE_CARD && iterPlaneCheck->_cards.size() > 1) { // 顺子
            int count = 0; // 拆掉了几个飞机
            int len = (int)(iterPlaneCheck->_cards.size());
            bool head = false;
            bool tail = false;
            for (int& one : iterPlaneCheck->_cards) {
                for(auto&& iter2 : theCardGroups) {
                    if((iter2._type == CARD_TYPE::THREE_CARD || iter2._type == CARD_TYPE::BOMB_CARD)
                       && std::find(iter2._cards.begin(), iter2._cards.end(), one) != iter2._cards.end()) {
                        if(one == iterPlaneCheck->_cards[ len - 1 ]) {
                            tail = true;
                        } else if(one == iterPlaneCheck->_cards[0]) {
                            head = true;
                        }
                        ++count;
                    }
                }
            }
            if(count >= 2) {
                if(len == 5 && (head || tail)) {
                    iterPlaneCheck = theCardGroups.erase(iterPlaneCheck);
                } else if(head && tail) {
                    if(len >= 7) {
                        iterPlaneCheck->_cards.resize(len - 1);
                        iterPlaneCheck->_cards.erase(iterPlaneCheck->_cards.begin());
                    } else if(len == 6) {
                        iterPlaneCheck->_cards.resize(5);
                    }
                } else if(tail && len >= 6) {
                    iterPlaneCheck->_cards.resize(len - 1);
                } else if(head && len >= 6) {
                    iterPlaneCheck->_cards.erase(iterPlaneCheck->_cards.begin());
                } else {
                    iterPlaneCheck = theCardGroups.erase(iterPlaneCheck);
                }
                continue;
            }
        }
        ++iterPlaneCheck;
    }
    // ========================  最后检查单牌（可以提供给飞机当作翅膀带出去） ========================
    // 这种牌只可能在 card_1 里面出现
    diff.clear();
    std::set_symmetric_difference(card_1.begin(),card_1.end(),card_2.begin(),card_2.end(),std::back_inserter(diff));
    // card_2 和 card_1 对比，留下的是纯粹的单牌
    if(!diff.empty()) {
        // 普通单牌
        for(int32_t& oneCard : diff) {
            theCardGroups.push_back({CARD_TYPE::SINGLE_CARD, {oneCard}});
        }
        // 能组成顺子的不算入绝对单牌，需要排除掉
        for(auto&& iter : theCardGroups) {
            if(iter._type == CARD_TYPE::SINGLE_CARD && iter._cards.size() > 1) {
                for(auto&& one : iter._cards) {
                    auto tmp = std::find(diff.begin(), diff.end(), one);
                    if(tmp != diff.end()) {
                        diff.erase(tmp);
                    }
                }
            }
        }
        // 大小王都不算做绝对单牌，需要排除掉
        auto tmp = std::find(diff.begin(), diff.end(), NUM_XW);
        if(tmp != diff.end()) {
            diff.erase(tmp);
        }
        tmp = std::find(diff.begin(), diff.end(), NUM_DW);
        if(tmp != diff.end()) {
            diff.erase(tmp);
        }
        if(!diff.empty()) {
            theCardGroups.push_back({ CARD_TYPE::ONLY_CARD, diff});
        }
    }
    // 三带X 和 四带X 不检查，出牌的时候再配对
//#if COCOS2D_DEBUG > 0
//    for(auto&& iter : theCardGroups) {
//        iter.toString();
//    }
//#endif
}

CARDS_DATA PokerLogic::CheckCardType(std::vector< int32_t >& cardsOut) {
    CARDS_DATA ret = { CARD_TYPE::ERROR_CARD, {} };
    std::vector<CARDS_DATA> theCardGroups;
    doAnalysis(cardsOut, theCardGroups);
    for(auto&& iter : theCardGroups) {
        if(iter._type > ret._type
           || (iter._type == ret._type && iter._cards.size() > ret._cards.size())
           || (iter._type == ret._type && iter._cards.size() == ret._cards.size() && iter._cards[0] > ret._cards[0])
        ) {
            ret._type = iter._type;
            ret._cards = iter._cards;
        }
    }
    auto full = cardsOut.size();
    auto count = ret._cards.size();
    CARD_TYPE theType = ret._type;
    switch(theType) {
        case CARD_TYPE::SINGLE_CARD :
        case CARD_TYPE::DOUBLE_CARD :
            // 单张，对子，顺子或者连对，不会有其他遗漏，张数对不上就是有错
            if(full != count) {
                ret._type = CARD_TYPE::ERROR_CARD;
            }
            break;
        case CARD_TYPE::THREE_CARD :
        case CARD_TYPE::THREE_ONE_CARD :
        case CARD_TYPE::THREE_TWO_CARD :
        case CARD_TYPE::BOMB_ONE_CARD :
        case CARD_TYPE::BOMB_TWO_CARD :
        case CARD_TYPE::BOMB_TWOO_CARD :
        case CARD_TYPE::BOMB_TWOOO_CARD :
        case CARD_TYPE::BOMB_CARD :
            if(full != count) {
                ret._type = CARD_TYPE::ERROR_CARD;//  悲观算法，先假设有错误，然后再检查是不是真的有错
                // count 对不上就是带翅膀！剩下的那就应该是 SINGLE_CARD 或者 DOUBLE_CARD
                std::vector<int32_t> vec_poke;
                for (auto&& one : cardsOut) {
                    vec_poke.emplace_back(one % 100);//去掉花色，留下数字
                }
                std::sort(vec_poke.begin(), vec_poke.end());
                std::vector<int32_t> diff;
                std::set_symmetric_difference(vec_poke.begin(),vec_poke.end(),ret._cards.begin(),ret._cards.end(),std::back_inserter(diff));
                doAnalysis(diff, theCardGroups);
                // 几个单张，几个对子
                int countSingle = (int)diff.size();
                int countDouble = 0;
                for(auto&& iter : theCardGroups) {
                    if(iter._type == CARD_TYPE::DOUBLE_CARD) {
                        countDouble += (int)(iter._cards.size() / 2);// 存在对子，累加对子个数
                    }
                }
                if(theType == CARD_TYPE::THREE_CARD) {
                    int len = (int)(count / 3);// len 为1就是单三张，否则就是连三
                    if(countDouble == len && full == len * 5) {
                        ret._type = CARD_TYPE::THREE_TWO_CARD;// 3带2，三张并带上一对
                    } else if(countSingle == len && full == len * 4) {
                        ret._type = CARD_TYPE::THREE_ONE_CARD;// 3带1，三张并带上任意一张牌
                    }
                } else {
                    int len = (int)(count / 4);// len 为1就是单四张，否则就是连四
                    if(countSingle == len && full == len * 5) {
                        ret._type = CARD_TYPE::BOMB_ONE_CARD;// 四张带上1个单张
                    } else if(countDouble == len && full == len * 6) {
                        ret._type = CARD_TYPE::BOMB_TWOO_CARD;// 四张带上1对
                    } else if(countSingle == len * 2 && full == len * 6) {
                        ret._type = CARD_TYPE::BOMB_TWO_CARD;// 四张带上2个单张
                    } else if(countDouble == len * 2 && full == len * 8) {
                        ret._type = CARD_TYPE::BOMB_TWOOO_CARD;// 四张带上2对
                    }
                }
                if(ret._type != CARD_TYPE::ERROR_CARD) {
                    std::copy(diff.begin(), diff.end(), std::back_inserter(ret._cards));
                }
            }
            break;
        case CARD_TYPE::MISSILE_CARD : // 火箭，也叫作王炸
            if(count != 2 || full != 2) {
                ret._type = CARD_TYPE::ERROR_CARD;// 火箭只能是2张，不会有其他遗漏，张数对不上就是有错
            }
            break;
        default:
            break;
    }
//    DebugLogWrite("CheckCardType: ");
//    if(ret._type != CARD_TYPE::ERROR_CARD) {
//        ret.toString();
//    } else {
//        DebugLogWrite("ERROR_CARD\n");
//    }
    return ret;
}

